package edu.umn.cs5115.scheduler;

import java.util.Hashtable;
import java.util.ArrayList;
import java.util.List;
import edu.umn.cs5115.scheduler.entities.Course;
import edu.umn.cs5115.scheduler.framework.IteratorSafeSet;
import edu.umn.cs5115.scheduler.framework.DocumentController;

public class SearchQuery {

	
	private Hashtable<String, Object> searchParams;

	IteratorSafeSet<SearchListener> listeners = new IteratorSafeSet<SearchListener>();

    /** The current search being executed. */
    private SearchThread search;

	public synchronized void addListener(SearchListener listener) {
		listeners.add(listener);
	}

	public synchronized void removeListener(SearchListener listener) {
		listeners.remove(listener);
	}

	public void sendSearchCompleted(final List<Course> results) {
		DocumentController.getSharedDocumentController().getDisplay().asyncExec(new Runnable() {
			public void run() {
				synchronized (SearchQuery.this) {
					listeners.startIterating();
					for (SearchListener listener : listeners)
						listener.searchCompleted(results);
					listeners.finishIterating();
				}
			}
		});
	}
	 
	public SearchQuery(){
		searchParams = new Hashtable<String, Object>();
	}

	public void searchTerm(String term) {
		
		searchParams.put("searchTerm", term);
	}
	
	public void addSubject(String subject){
		addToKey("searchSubjects", subject);
	}  
	
	public void searchCourseNumber(String courseNumber) {
		
		searchParams.put("searchCatalogNumber", courseNumber);
	}
	
	public void searchIDLOnly(Boolean IDL) {
		
		searchParams.put("searchIDLOnly", IDL.toString());
	}
	
	public void searchOpenSections(Boolean searchOpenSections) {
		
		searchParams.put("searchOpenSections", searchOpenSections.toString());
	}
	
	// 24 hour format, prefixed with 0
	public void searchStartTime(String startTime) {
		searchParams.put("searchLowerStartTime", startTime);
	}
	
	// 24 hour format, prefixed with 0
	public void searchEndTime(String endTime) {
		searchParams.put("searchUpperEndTime", endTime);
	}
	
	// expects a day and whether or not to search it.
	// day is in 3 letter format, first letter capitalized
	public void searchDay(String day, Boolean value) {
		searchParams.put("search".concat(day), value.toString()); 
	}
	
	public void searchLowerLevelLimit(String limit) {
		searchParams.put("searchLowerLevelLimit", limit.replace("xxx", "000"));
	}

	public void searchUpperLevelLimit(String limit) {
		searchParams.put("searchUpperLevelLimit", limit.replace("xxx", "999"));
	}
	
	// should be less than 9, but I don't check for that since the web search
	// technically takes higher entries.
	public void searchLowerCreditLimit(String limit) {
		searchParams.put("searchLowerCreditLimit", limit);
	}
	
	public void searchUpperCreditLimit(String limit) {
		
		// this is how the web site handles the upper limit...
		if(limit.equals("9+"))
			searchParams.put("searchUpperCreditLimit", "9999");
		else
			searchParams.put("searchUpperCreditLimit", limit);
	}

	public void searchInstructorName(String name) {
		searchParams.put("searchInstructorName", name);
	}

	public void searchCourseTitle(String title) {
		searchParams.put("searchCourseTitle", title);
	}
	
	public void searchHonorsOnly(Boolean value) {
		searchParams.put("searchHonorsOnly", value.toString());
	}
	
	public void searchTeachingAwardOnly(Boolean value) {
		searchParams.put("searchTeachingAwardWinnerOnly", value.toString());
	}
	
	public void searchFullYearOnly(Boolean value) {
		searchParams.put("searchFullYearEnrollmentOnly", value.toString());
	}
	
	public void addLibEdReq(String libEd) {
		addToKey("searchCourseAttributes", libEd);
	}
	
	public void addSession(String session) {
		addToKey("searchSessionCodes", session);
	}
	
	public void addCampusArea(String area) {
		addToKey("searchLocations", area);
	}
	
	public String toString() {
		return searchParams.toString();
	}
	

	// Following for setting up fields that can have multiple selections.
	private void initializeKey(String key) {
		if(!searchParams.containsKey(key)) {
			searchParams.put(key, new ArrayList<String>());
		}
	}
	
	private void addToKey(String key, String value){
		initializeKey(key);
		
		// if anyone knows how to remove this warning, let me know.
		((ArrayList<String>)searchParams.get(key)).add(value);
	}
	
	public Hashtable<String, Object> getParams() {
		return searchParams;
	}

    /** Sets the search thread executing the search of this query.  This method
     * can only be called once.
     * @param searcher The new search thread.
     */
    void setSearch(SearchThread searcher)
    {
        if (search == null)
            search = searcher;
        else
            throw new IllegalStateException("You can only set search once for each query.");
    }
    
    /**
     * Cancels the search.  Makes sure that searchCompleted will never be called
     * on any listener.
     */
    public void cancelSearch()
    {
        if (search.isAlive())
            search.interrupt();
    }
	
}
